package gruppe5.dame.server.rfc;

import java.util.ArrayList;
import java.util.List;

import javax.swing.JOptionPane;

import general.socketmanager.cSocket;
import general.socketmanager.cSocketManager;
import general.socketmanager.iSocketListener;
import gruppe5.dame.server.rfc.objects.cPackageHeader;
import gruppe5.dame.server.rfc.objects.cLoginRequest;
import gruppe5.dame.server.rfc.objects.cLoginResponse;
import gruppe5.dame.server.rfc.objects.cLobby;
import gruppe5.dame.server.rfc.objects.cChatMessage;
import gruppe5.dame.server.rfc.objects.cGameCreateRequest;
import gruppe5.dame.server.rfc.objects.cGameJoinRequest;
import gruppe5.dame.server.rfc.objects.cSpielfeld;
import gruppe5.dame.server.rfc.objects.cGameMove;
import gruppe5.dame.server.rfc.objects.cError;

// This class provides the project with methods, functions and enumerations to work with the rfc network packages
public class cRFC implements iSocketListener {
	private cSocketManager oSocketManager;
	
	private List<iRFCListener> oListener;
	
	private boolean flag_Server_IsListening = false;
	private boolean flag_Client_IsConnected = false;
	
	private int data_Server_SocketID = -1;
	private int data_Server_ListeningPort = -1;
	private ArrayList<Integer> data_Server_ClientIDs = new ArrayList<Integer>();
	private String data_Client_ConnectedIP = "";
	private int data_Client_ConnectedPort = -1;
	private int data_Client_SocketID = -1;

	
	
	public cRFC() {
		oSocketManager = new cSocketManager();
		oSocketManager.AddListener(this);
		
		oListener = new ArrayList<iRFCListener>();
	}
	
	// Adds a listener to the monitor the sockets
	public void AddListener(iRFCListener oListener) {
		this.oListener.add(oListener);
	}

	
	public boolean Server_StartListening(int iPort) {
		if (flag_Server_IsListening) return false;
		
		int iTemp;
		
		iTemp = oSocketManager.NewServerSocket(iPort);
		if (iTemp > -1) {
			// success
			flag_Server_IsListening = true;
			data_Server_SocketID = iTemp;
			data_Server_ListeningPort = iPort;
			return true;
		} else {
			// failed
			return false;
		}
	}
	
	public boolean Server_StopListening() {
		if (!flag_Server_IsListening) return false;
		
		if (oSocketManager.DisconnectSocket(data_Server_SocketID)) {
			// success
			flag_Server_IsListening = false;
			data_Server_SocketID = -1;
			data_Server_ListeningPort = -1;
			return true;
		} else {
			// failed
			return false;
		}
	}
	
	public void Server_DisconnectAllClients() {
		for (int i = 0; i < data_Server_ClientIDs.size(); i++) {
			oSocketManager.DisconnectSocket(data_Server_ClientIDs.get(i));
		}
	}
	
	public boolean Server_DisconnectClient(int iSocketID) {
		for (int i = 0; i < data_Server_ClientIDs.size(); i++) {
			if (iSocketID == data_Server_ClientIDs.get(i)) {
				if (oSocketManager.DisconnectSocket(iSocketID)) {
					data_Server_ClientIDs.remove(i);
					return true;
				}
			}
			
		}
		return false;
	}
	
	public boolean Server_SendPackage(int iClientSocketID, cPackageHeader oPackage) {
		System.out.println("size : " + data_Server_ClientIDs.size());
		System.out.println("searching id : " + iClientSocketID);
		for (int i = 0; i < data_Server_ClientIDs.size(); i++) {
			System.out.println(iClientSocketID + " == " + data_Server_ClientIDs.get(i));
			
			if (iClientSocketID == data_Server_ClientIDs.get(i)) {
				System.out.println("trying to send...");
				return oSocketManager.SendObject(iClientSocketID, oPackage);
			}
		}
		return false;
	}
	
	public boolean Server_IsListening() {
		return flag_Server_IsListening;
	}
	
	public int Server_GetListeningPort() {
		return data_Server_ListeningPort;
	}
	
	public int[] Server_GetAllClientIDs() {
		if (data_Server_ClientIDs.size() == 0) return null;
		
		int[] iIDs = new int[data_Server_ClientIDs.size()];
		for (int i = 0; i < data_Server_ClientIDs.size(); i++) {
			iIDs[i] = data_Server_ClientIDs.get(i);
		}
		
		return iIDs;
	}
	
	// Connects the client to a remote server with given sIP and iPort
	// Returns true if successfully connected and false otherwise
	public boolean Client_ConnectTo(String sIP, int iPort) {
		int iTemp;
		
		if (flag_Client_IsConnected) {
			// already connected
			return false;
		} else {
			iTemp = oSocketManager.NewClientSocket(sIP, iPort);
			if (iTemp > -1) {
				// connected successfully
				flag_Client_IsConnected = true;
				data_Client_ConnectedIP = sIP;
				data_Client_ConnectedPort = iPort;
				data_Client_SocketID = iTemp;
				return true;
			} else {
				// connection failed
				return false;
			}
		}
	}
	
	// Disconnects the client from a server
	// Returns true if successfully disconnected and false otherwise
	public boolean Client_Disconnect() {
		if (flag_Client_IsConnected) {
			if (oSocketManager.DisconnectSocket(data_Client_SocketID)) {
				// disconnected successfully
				flag_Client_IsConnected = false;
				data_Client_ConnectedIP = "";
				data_Client_ConnectedPort = -1;
				data_Client_SocketID = -1;
				return true;
			} else {
				// disconnect failed
				return false;
			}
		} else {
			// not currently connected
			return false;
		}
	}
	
	// Sends a given oPackage with type eType to the server.
	// Returns true if successful and false otherwise
	public boolean Client_SendPackage(cPackageHeader oPackage) {
		if (!flag_Client_IsConnected) { return false; }
		return oSocketManager.SendObject(data_Client_SocketID, oPackage);
	}
	
	// Returns true if the client is connected, false otherwise
	public boolean Client_IsConnected() {
		return flag_Client_IsConnected;
	}
	
	// Returns the ip address the client is currently connected to (if connected)
	public String Client_GetConnectedIP() {
		return data_Client_ConnectedIP;
	}
	
	// Returns the port the client is currently connected to (if connected)
	public int Client_GetConnectedPort() {
		return data_Client_ConnectedPort;
	}
	
	//////////////////////////////////////////////////
	////////// PRIVATE METHODS
	//////////////////////////////////////////////////
	
	// Fires all OnServer_Listening() events
	private void events_Fire_OnServer_Listening(int iSocketID) {
		for (int i = 0; i < oListener.size(); i++) {
			oListener.get(i).OnServer_Listening(iSocketID);
		}
	}
	
	// Fires all OnServer_NewClient() events
	private void events_Fire_OnServer_NewClient(int iServerSocketID, int iNewClientSocketID) {
		for (int i = 0; i < oListener.size(); i++) {
			oListener.get(i).OnServer_NewClient(iServerSocketID, iNewClientSocketID);
		}
	}
	
	// Fires all OnClient_ConnectionEstablished() events
	private void events_Fire_OnClient_ConnectionEstablished(int iSocketID) {
		for (int i = 0; i < oListener.size(); i++) {
			oListener.get(i).OnAll_ConnectionEstablished(iSocketID);
		}
	}

	// Fires all OnServer_NewConnection() events
	private void events_Fire_OnClient_ObjectReceived(int iSocketID, cPackageHeader.PackageType eType, cPackageHeader oObject) {
		for (int i = 0; i < oListener.size(); i++) {
			oListener.get(i).OnAll_ObjectReceived(iSocketID, eType, oObject);
		}
	}
	
	// Fires all OnServer_NewConnection() events
	private void events_Fire_OnClient_LoginRequestReceived(int iSocketID, cLoginRequest oObject) {
		for (int i = 0; i < oListener.size(); i++) {
			oListener.get(i).OnAll_LoginRequestReceived(iSocketID, oObject);
		}
	}
	
	// Fires all OnServer_NewConnection() events
	private void events_Fire_OnClient_LoginResponseReceived(int iSocketID, cLoginResponse oObject) {
		for (int i = 0; i < oListener.size(); i++) {
			oListener.get(i).OnAll_LoginResponseReceived(iSocketID, oObject);
		}
	}
	
	// Fires all OnServer_NewConnection() events
	private void events_Fire_OnClient_LobbyReceived(int iSocketID, cLobby oObject) {
		for (int i = 0; i < oListener.size(); i++) {
			oListener.get(i).OnAll_LobbyReceived(iSocketID, oObject);
		}
	}
	
	// Fires all OnServer_NewConnection() events
	private void events_Fire_OnClient_ChatMessageReceived(int iSocketID, cChatMessage oObject) {
		for (int i = 0; i < oListener.size(); i++) {
			oListener.get(i).OnAll_ChatMessageReceived(iSocketID, oObject);
		}
	}
	
	// Fires all OnServer_NewConnection() events
	private void events_Fire_OnClient_GameCreateRequestReceived(int iSocketID, cGameCreateRequest oObject) {
		for (int i = 0; i < oListener.size(); i++) {
			oListener.get(i).OnAll_GameCreateRequestReceived(iSocketID, oObject);
		}
	}
	
	// Fires all OnServer_NewConnection() events
	private void events_Fire_OnClient_GameJoinRequestReceived(int iSocketID, cGameJoinRequest oObject) {
		for (int i = 0; i < oListener.size(); i++) {
			oListener.get(i).OnAll_GameJoinRequestReceived(iSocketID, oObject);
		}
	}
	
	// Fires all OnServer_NewConnection() events
	private void events_Fire_OnClient_SpielfeldReceived(int iSocketID, cSpielfeld oObject) {
		for (int i = 0; i < oListener.size(); i++) {
			oListener.get(i).OnAll_SpielfeldReceived(iSocketID, oObject);
		}
	}
	
	// Fires all OnServer_NewConnection() events
	private void events_Fire_OnClient_GameMoveReceived(int iSocketID, cGameMove oObject) {
		for (int i = 0; i < oListener.size(); i++) {
			oListener.get(i).OnAll_GameMoveReceived(iSocketID, oObject);
		}
	}
	
	// Fires all OnServer_NewConnection() events
	private void events_Fire_OnClient_ErrorReceived(int iSocketID, cError oObject) {
		for (int i = 0; i < oListener.size(); i++) {
			oListener.get(i).OnAll_ErrorReceived(iSocketID, oObject);
		}
	}
	
	// Fires all OnAll_Disconnected() events
	private void events_Fire_OnClient_Disconnected(int iSocketID) {
		for (int i = 0; i < oListener.size(); i++) {
			oListener.get(i).OnAll_Disconnected(iSocketID);
		}
	}
	
	//////////////////////////////////////////////////
	////////// LISTENER IMPLEMENTATION
	//////////////////////////////////////////////////
	
	@Override
	public void OnServer_Listening(int iSocketID) {
		// TODO Auto-generated method stub
		flag_Server_IsListening = true;
		data_Server_SocketID = iSocketID;
		events_Fire_OnServer_Listening(iSocketID);
	}

	@Override
	public void OnServer_NewClient(int iServerSocketID, int iNewClientSocketID) {
		// TODO Auto-generated method stub
		data_Server_ClientIDs.add(iNewClientSocketID);
		events_Fire_OnServer_NewClient(iServerSocketID, iNewClientSocketID);
		
	}

	@Override
	public void OnClient_ConnectionEstablished(int iSocketID) {
		// TODO Auto-generated method stub
		flag_Client_IsConnected = true;
		data_Client_SocketID = iSocketID;
		events_Fire_OnClient_ConnectionEstablished(iSocketID);
	}

	@Override
	public void OnClient_DataReceived(int iSocketID, byte[] aBytes) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void OnClient_ObjectReceived(int iSocketID, Object oObject) {
		// TODO Auto-generated method stub
		
		events_Fire_OnClient_ObjectReceived(iSocketID, ((cPackageHeader)oObject).type, (cPackageHeader)oObject);
		
		switch (((cPackageHeader)oObject).type) {
		
			case LoginRequest:
				events_Fire_OnClient_LoginRequestReceived(iSocketID, (cLoginRequest)oObject);
				break;
			case LoginResponse:
				events_Fire_OnClient_LoginResponseReceived(iSocketID, (cLoginResponse)oObject);
				break;
			case Lobby:
				events_Fire_OnClient_LobbyReceived(iSocketID, (cLobby)oObject);
				break;
			case ChatMessage:
				events_Fire_OnClient_ChatMessageReceived(iSocketID, (cChatMessage)oObject);
				break;
			case GameCreateRequest:
				events_Fire_OnClient_GameCreateRequestReceived(iSocketID, (cGameCreateRequest)oObject);
				break;
			case GameJoinRequest:
				events_Fire_OnClient_GameJoinRequestReceived(iSocketID, (cGameJoinRequest)oObject);
				break;
			case Spielfeld:
				events_Fire_OnClient_SpielfeldReceived(iSocketID, (cSpielfeld)oObject);
				break;
			case GameMove:
				events_Fire_OnClient_GameMoveReceived(iSocketID, (cGameMove)oObject);
				break;
			case Error:
				events_Fire_OnClient_ErrorReceived(iSocketID, (cError)oObject);
				break;
			
			default:
			
				break;
		}
	}

	@Override
	public void OnClient_ConnectionClosed(int iSocketID) {
		// TODO Auto-generated method stub
		events_Fire_OnClient_Disconnected(iSocketID);
	}



}
